package org.jeecg.modules.workflow.service.task;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.SecurityUtils;
import org.flowable.bpmn.model.*;
import org.flowable.bpmn.model.Process;
import org.flowable.engine.*;
import org.flowable.engine.history.HistoricActivityInstance;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.runtime.Execution;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.task.api.Task;
import org.flowable.task.api.TaskQuery;
import org.flowable.task.api.history.HistoricTaskInstance;
import org.flowable.task.api.history.HistoricTaskInstanceQuery;
import org.jeecg.common.exception.ServiceException;
import org.jeecg.common.system.api.ISysBaseAPI;
import org.jeecg.common.system.vo.LoginUser;
import org.jeecg.common.util.collection.CollectionUtils;
import org.jeecg.common.workflow.vo.task.task.*;
import org.jeecg.modules.workflow.convert.task.BpmTaskConvert;
import org.jeecg.modules.workflow.entity.BpmProcessDefinitionExt;
import org.jeecg.modules.workflow.entity.BpmTaskExt;
import org.jeecg.modules.workflow.enums.task.BpmProcessInstanceDeleteReasonEnum;
import org.jeecg.modules.workflow.enums.task.BpmProcessInstanceResultEnum;
import org.jeecg.modules.workflow.framework.activiti.core.util.FlowableUtils;
import org.jeecg.modules.workflow.mapper.BpmTaskExtMapper;
import org.jeecg.modules.workflow.service.definition.BpmProcessDefinitionService;
import org.jeecg.modules.workflow.utils.PageUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionSynchronization;
import org.springframework.transaction.support.TransactionSynchronizationManager;

import javax.annotation.Resource;
import javax.validation.Valid;
import java.util.*;
import java.util.concurrent.atomic.AtomicReference;

import static org.jeecg.common.exception.util.ServiceExceptionUtil.exception;
import static org.jeecg.common.util.collection.CollectionUtils.*;
import static org.jeecg.modules.workflow.enums.ErrorCodeConstants.*;

/**
 * 流程任务实例 Service 实现类
 *
 * @author jason
 * @author 芋道源码
 */
@Slf4j
@Service
public class BpmTaskServiceImpl implements BpmTaskService {

    @Resource
    private TaskService taskService;
    @Resource
    private HistoryService historyService;

    @Resource
    private ISysBaseAPI sysBaseApi;

    @Resource
    @Lazy // 解决循环依赖
    private BpmProcessInstanceService processInstanceService;

    @Resource
    private BpmTaskExtMapper taskExtMapper;

    @Autowired
    BpmTaskConvert bpmTaskConvert;

    @Resource
    private ProcessEngine processEngine;

    @Resource
    private RepositoryService repositoryService;

    @Resource
    private RuntimeService runtimeService;

    @Resource
    private BpmProcessDefinitionService processDefinitionService;

    @Override
    public List<Task> getRunningTaskListByProcessInstanceId(String processInstanceId) {
        return taskService.createTaskQuery().processInstanceId(processInstanceId).list();
    }

    private List<BpmTaskRespVO> getBpmTaskRespVOSByTasks(List<HistoricTaskInstance> tasks, String processInstanceId) {
        // 获得 TaskExtDO Map
        List<BpmTaskExt> BpmTaskExts = taskExtMapper.selectListByTaskIds(convertList(tasks, HistoricTaskInstance::getId));
        Map<String, BpmTaskExt> BpmTaskExtMap = convertMap(BpmTaskExts, BpmTaskExt::getTaskId);
        // 获得 ProcessInstance Map
        HistoricProcessInstance processInstance = processInstanceService.getHistoricProcessInstance(processInstanceId);
        // 获得 User Map
        List<String> userIds = convertList(tasks, task -> task.getAssignee());
        userIds.add(processInstance.getStartUserId());
        Map<String, LoginUser> userMap = getUserMap(userIds);

        //会签自动取消的任务不显示
        for (Iterator<HistoricTaskInstance> ite = tasks.iterator(); ite.hasNext(); ) {
            HistoricTaskInstance historicTaskInstance = ite.next();
            if ("MI_END".equals(historicTaskInstance.getDeleteReason())) {
                ite.remove();
            }
        }
        // 拼接数据
        List<BpmTaskRespVO> bpmTaskRespVOS = bpmTaskConvert.convertList3(tasks, BpmTaskExtMap, processInstance, userMap);
        return bpmTaskRespVOS;
    }

    @Override
    public List<BpmTaskRespVO> getTaskListByProcessInstanceId(String processInstanceId) {
        // 获得任务列表
        List<HistoricTaskInstance> tasksOrigin = historyService.createHistoricTaskInstanceQuery()
                .processInstanceId(processInstanceId)
                .orderByHistoricTaskInstanceStartTime().desc() // 创建时间倒序
                .list();
        if (CollUtil.isEmpty(tasksOrigin)) {
            return Collections.emptyList();
        }
        List<BpmTaskRespVO> bpmTaskRespVOS = getBpmTaskRespVOSByTasks(tasksOrigin, processInstanceId);
        //待处理的任务,判断当前用户有没有审批权限
        LoginUser user = (LoginUser) SecurityUtils.getSubject().getPrincipal();
        for (Iterator<BpmTaskRespVO> ite = bpmTaskRespVOS.iterator(); ite.hasNext(); ) {
            BpmTaskRespVO bpmTaskRespVO = ite.next();
            //驳回,回退的不显示
            if (bpmTaskRespVO.getComment() != null && bpmTaskRespVO.getComment().startsWith("Change parent activity")) {
                ite.remove();
                continue;
            }
            if (BpmProcessInstanceResultEnum.PROCESS.getResult().equals(bpmTaskRespVO.getResult())) {
                Task taskCheck = taskService.createTaskQuery().taskId(bpmTaskRespVO.getId())
                        .or().taskCandidateUser(user.getId()).taskAssignee(user.getId()).endOr().singleResult();
                if (taskCheck != null) {
                    bpmTaskRespVO.setTaskCheck(true);
                }
            }
        }

        return bpmTaskRespVOS;
    }

    @Override
    public List<Task> getTasksByProcessInstanceId(String processInstanceId) {
        if (StrUtil.isEmpty(processInstanceId)) {
            return Collections.emptyList();
        }
        return taskService.createTaskQuery().processInstanceId(processInstanceId).list();
    }

    @Override
    public List<Task> getTasksByProcessInstanceIds(List<String> processInstanceIds) {
        if (CollUtil.isEmpty(processInstanceIds)) {
            return Collections.emptyList();
        }
        return taskService.createTaskQuery().processInstanceIdIn(processInstanceIds).list();
    }

    @Override
    public IPage<BpmTaskTodoPageItemRespVO> getTodoTaskPage(String userId, BpmTaskTodoPageReqVO pageVO) {
        // 查询待办任务
        /*LoginUser user = SecurityFrameworkUtils.getLoginUser();
        Set<Long> roleIds = user.getRoleIds();
        List<String> candidateGroups = new ArrayList<>();
        for(Long roleId : roleIds){
            candidateGroups.add(roleId+"");
        }*/
        TaskQuery taskQuery = taskService.createTaskQuery()
                .or().taskCandidateUser(String.valueOf(userId)).taskAssignee(String.valueOf(userId)).endOr()
                // 分配给自己
                .orderByTaskCreateTime().desc(); // 创建时间倒序
        if (StrUtil.isNotBlank(pageVO.getName())) {
            taskQuery.taskNameLike("%" + pageVO.getName() + "%");
        }
        if (pageVO.getBeginCreateTime() != null) {
            taskQuery.taskCreatedAfter(pageVO.getBeginCreateTime());
        }
        if (pageVO.getEndCreateTime() != null) {
            taskQuery.taskCreatedBefore(pageVO.getEndCreateTime());
        }
        // 执行查询
        List<Task> tasks = taskQuery.listPage(PageUtils.getStart(pageVO), pageVO.getPageSize());
        if (CollUtil.isEmpty(tasks)) {
            return new Page<>();
        }

        // 获得 ProcessInstance Map
        Map<String, ProcessInstance> processInstanceMap = processInstanceService.getProcessInstanceMap(
                convertSet(tasks, Task::getProcessInstanceId));
        // 获得 User Map
        Map<String, LoginUser> userMap = getUserMap(
                convertList(processInstanceMap.values(), instance -> instance.getStartUserId()));
        // 拼接结果
        Page<BpmTaskTodoPageItemRespVO> result = new Page<>();
        result.setRecords(bpmTaskConvert.convertList1(tasks, processInstanceMap, userMap));
        result.setTotal(taskQuery.count());
        return result;
    }

    @Override
    public IPage<BpmTaskTodoPageItemRespVO> getTodoTaskPage(String userId, String processName, BpmTaskTodoPageReqVO pageVO) {
        TaskQuery taskQuery = taskService.createTaskQuery()
                .or().taskCandidateUser(String.valueOf(userId)).taskAssignee(String.valueOf(userId)).endOr()   // 分配给自己
                .processDefinitionName(processName)     //流程定义Id
                .orderByTaskCreateTime().desc();        // 创建时间倒序
        if (StrUtil.isNotBlank(pageVO.getName())) {
            taskQuery.taskNameLike("%" + pageVO.getName() + "%");
        }
        if (pageVO.getBeginCreateTime() != null) {
            taskQuery.taskCreatedAfter(pageVO.getBeginCreateTime());
        }
        if (pageVO.getEndCreateTime() != null) {
            taskQuery.taskCreatedBefore(pageVO.getEndCreateTime());
        }
        // 执行查询
        List<Task> tasks = taskQuery.listPage(PageUtils.getStart(pageVO), pageVO.getPageSize());
        if (CollUtil.isEmpty(tasks)) {
            return new Page<>();
        }

        // 获得 ProcessInstance Map
        Map<String, ProcessInstance> processInstanceMap = processInstanceService.getProcessInstanceMap(
                convertSet(tasks, Task::getProcessInstanceId));
        // 获得 User Map
        Map<String, LoginUser> userMap = getUserMap(
                convertList(processInstanceMap.values(), instance -> instance.getStartUserId()));
        // 拼接结果
        Page<BpmTaskTodoPageItemRespVO> result = new Page<>();
        result.setRecords(bpmTaskConvert.convertList1(tasks, processInstanceMap, userMap));
        result.setTotal(taskQuery.count());
        return result;
    }

    @Override
    public List<Task> getAllTodoTask(String userId) {
        return taskService.createTaskQuery()
                .or().taskCandidateUser(String.valueOf(userId)).taskAssignee(String.valueOf(userId)).endOr()
                .orderByTaskCreateTime().desc().list();
    }

    @Override
    public Page<BpmTaskDonePageItemRespVO> getDoneTaskPage(String userId, BpmTaskDonePageReqVO pageVO) {
        // 查询已办任务
        HistoricTaskInstanceQuery taskQuery = historyService.createHistoricTaskInstanceQuery()
                .finished() // 已完成
                .or().taskCandidateUser(String.valueOf(userId)).taskAssignee(String.valueOf(userId)).endOr() // 分配给自己
                .orderByHistoricTaskInstanceEndTime().desc(); // 审批时间倒序
        if (StrUtil.isNotBlank(pageVO.getName())) {
            taskQuery.taskNameLike("%" + pageVO.getName() + "%");
        }
        if (pageVO.getBeginCreateTime() != null) {
            taskQuery.taskCreatedAfter(pageVO.getBeginCreateTime());
        }
        if (pageVO.getEndCreateTime() != null) {
            taskQuery.taskCreatedBefore(pageVO.getEndCreateTime());
        }
        // 执行查询
        List<HistoricTaskInstance> tasks = taskQuery.listPage(PageUtils.getStart(pageVO), pageVO.getPageSize());
        if (CollUtil.isEmpty(tasks)) {
            return new Page<>();
        }

        // 获得 TaskExtDO Map
        List<BpmTaskExt> BpmTaskExts = taskExtMapper.selectListByTaskIds(convertList(tasks, HistoricTaskInstance::getId));
        Map<String, BpmTaskExt> BpmTaskExtMap = convertMap(BpmTaskExts, BpmTaskExt::getTaskId);
        // 获得 ProcessInstance Map
        Map<String, HistoricProcessInstance> historicProcessInstanceMap = processInstanceService.getHistoricProcessInstanceMap(
                convertSet(tasks, HistoricTaskInstance::getProcessInstanceId));
        // 获得 User Map
        Map<String, LoginUser> userMap = getUserMap(
                convertList(historicProcessInstanceMap.values(), instance -> instance.getStartUserId()));
        // 拼接结果
        Page<BpmTaskDonePageItemRespVO> result = new Page<>();
        result.setRecords(bpmTaskConvert.convertList2(tasks, BpmTaskExtMap, historicProcessInstanceMap, userMap));
        result.setTotal(taskQuery.count());
        return result;
    }

    @Override
    public void updateTaskAssignee(String userId, BpmTaskUpdateAssigneeReqVO reqVO) {
        // 校验任务存在
        Task task = checkTask(userId, reqVO.getId());

        // 更新负责人
        updateTaskAssignee(task.getId(), reqVO.getAssigneeUserId());
    }

    @Override
    public void updateTaskAssignee(String id, String userId) {
        taskService.setAssignee(id, userId);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void approveTask(String userId, BpmTaskApproveReqVO reqVO) {
        // 校验任务存在
        Task task = checkTask(userId, reqVO.getId());

        // 校验流程实例存在
        ProcessInstance instance = processInstanceService.getProcessInstance(task.getProcessInstanceId());
        if (instance == null) {
            throw exception(PROCESS_INSTANCE_NOT_EXISTS);
        }

        //拾取任务
        taskService.claim(reqVO.getId(), String.valueOf(userId));

        // 完成任务，审批通过
        taskService.complete(task.getId(), instance.getProcessVariables()); // TODO 芋艿：variables 的选择
        // 更新任务拓展表为通过
        BpmTaskExt bpmTaskExt = new BpmTaskExt();
        bpmTaskExt.setTaskId(task.getId());
        bpmTaskExt.setResult(BpmProcessInstanceResultEnum.APPROVE.getResult());
        bpmTaskExt.setRemark(reqVO.getComment());
        taskExtMapper.updateByTaskId(bpmTaskExt);

        // TODO 芋艿：添加评论
//        taskService.addComment(task.getId(), task.getProcessInstanceId(), reqVO.getComment());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void rejectTask(String userId, @Valid BpmTaskRejectReqVO reqVO) {
        // 校验任务存在
        Task task = checkTask(userId, reqVO.getId());

        // 校验流程实例存在
        ProcessInstance instance = processInstanceService.getProcessInstance(task.getProcessInstanceId());
        if (instance == null) {
            throw exception(PROCESS_INSTANCE_NOT_EXISTS);
        }

        //拾取任务
        taskService.claim(reqVO.getId(), String.valueOf(userId));

        // 更新流程实例为不通过
        processInstanceService.updateProcessInstanceExtReject(instance.getProcessInstanceId(), reqVO.getComment());

        // 更新任务拓展表为不通过
        BpmTaskExt bpmTaskExt = new BpmTaskExt();
        bpmTaskExt.setTaskId(task.getId());
        bpmTaskExt.setResult(BpmProcessInstanceResultEnum.REJECT.getResult());
        bpmTaskExt.setRemark(reqVO.getComment());
        taskExtMapper.updateByTaskId(bpmTaskExt);

    }

    private Task getTask(String id) {
        return taskService.createTaskQuery().taskId(id).singleResult();
    }

    // ========== Task 拓展表相关 ==========

    @Override
    public void createTaskExt(org.flowable.task.api.Task task) {
        // 插入 BpmTaskExt 记录
        BpmTaskExt taskExtDO = bpmTaskConvert.convert(task);
        taskExtDO.setResult(BpmProcessInstanceResultEnum.PROCESS.getResult());
        taskExtDO.setCreateBy(getUserName());
        taskExtDO.setCreateTime(new Date());
        taskExtMapper.insert(taskExtDO);
    }

    @Override
    public void updateTaskExt(org.flowable.task.api.Task task) {
        BpmTaskExt taskExtDO = bpmTaskConvert.convert(task);
        taskExtMapper.updateByTaskId(taskExtDO);
    }

    @Override
    public void updateTaskExtAssign(org.flowable.task.api.Task task) {
        // 更新
        updateTaskExt(task);
        // 发送通知。由于 Activiti 操作是在事务提交时，批量执行操作，所以直接查询会无法查询到 ProcessInstance，所以这里是通过监听事务的提交来实现。
       /* TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
            @Override
            public void afterCommit() {
                ProcessInstance processInstance = processInstanceService.getProcessInstance(task.getProcessInstanceId());
                AdminUserRespDTO startUser = adminUserApi.getUser(Long.valueOf(processInstance.getStartUserId()));
                messageService.sendMessageWhenTaskAssigned(bpmTaskConvert.convert(processInstance, startUser, task));
            }
        });*/
    }

    @Override
    public void updateTaskExtCancel(String taskId) {
        // 需要在事务提交后，才进行查询。不然查询不到历史的原因
        TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {

            @Override
            public void afterCommit() {
                // 可能只是活动，不是任务，所以查询不到
                HistoricTaskInstance task = getHistoricTask(taskId);
                if (task == null) {
                    return;
                }

                // 如果任务拓展表已经是完成的状态，则跳过
                BpmTaskExt taskExtQuery = new BpmTaskExt();
                taskExtQuery.setTaskId(taskId);
                BpmTaskExt taskExt = taskExtMapper.selectByTaskId(taskId);
                if (taskExt == null) {
                    log.error("[updateTaskExtCancel][taskId({}) 查找不到对应的记录，可能存在问题]", taskId);
                    return;
                }
                // 如果已经是最终的结果，则跳过
                if (BpmProcessInstanceResultEnum.isEndResult(taskExt.getResult())) {
                    log.error("[updateTaskExtCancel][taskId({}) 处于结果({})，无需进行更新]", taskId, taskExt.getResult());
                    return;
                }

                // 更新任务
                // 更新任务
                BpmTaskExt taskExtDO = new BpmTaskExt();
                taskExtDO.setId(taskExt.getId());
                taskExtDO.setResult(BpmProcessInstanceResultEnum.CANCEL.getResult());
                taskExtDO.setEndTime(new Date());
                taskExtDO.setRemark(BpmProcessInstanceDeleteReasonEnum.translateReason(task.getDeleteReason()));
                taskExtMapper.updateById(taskExtDO);
            }

        });
    }

    @Override
    public void updateTaskExtComplete(org.flowable.task.api.Task task) {
        BpmTaskExt taskExtDO = bpmTaskConvert.convert(task);
        taskExtDO.setEndTime(new Date()); // 此时不能使用 task 的 completeData，因为还是空的。
        taskExtDO.setResult(BpmProcessInstanceResultEnum.APPROVE.getResult());
        taskExtMapper.updateByTaskId(taskExtDO);
    }

    @Override
    public List<BpmTaskExt> getTaskExtListByProcessInstanceId(String processInstanceId) {
        return taskExtMapper.selectListByProcessInstanceId(processInstanceId);
    }

    @Override
    public Long getTaskCountByUserIdAndDefId(String userId, String defId) {
        return taskService.createTaskQuery().or().taskCandidateUser(String.valueOf(userId)).taskAssignee(String.valueOf(userId)).endOr().processDefinitionId(defId).count();
    }

    private Map<String, LoginUser> getUserMap(List<String> userIdList) {
        String[] userIds = userIdList.toArray(new String[userIdList.size()]);
        List<LoginUser> loginUsers = sysBaseApi.queryAllUserByIds(userIds);
        return CollectionUtils.convertMap(loginUsers, LoginUser::getId);
    }

    private String getUserName() {
        LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
        return sysUser.getRealname();
    }


    private HistoricTaskInstance getHistoricTask(String id) {
        return historyService.createHistoricTaskInstanceQuery().taskId(id).singleResult();
    }

    /**
     * 校验任务是否存在， 并且是否是分配给自己的任务
     *
     * @param userId 用户 id
     * @param taskId task id
     */
    private Task checkTask(String userId, String taskId) {
        Task taskCheck = taskService.createTaskQuery().taskId(taskId)
                .or().taskCandidateUser(String.valueOf(userId)).taskAssignee(String.valueOf(userId)).endOr().singleResult();
        if (taskCheck == null) {
            throw exception(TASK_COMPLETE_FAIL_ASSIGN_NOT_SELF);
        }
        return taskCheck;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void flowTackback(String userId, @Valid BpmTaskApproveReqVO reqVO) {
        // 校验任务存在
        Task task = checkTask(userId, reqVO.getId());
        // 校验流程实例存在
        ProcessInstance instance = processInstanceService.getProcessInstance(task.getProcessInstanceId());
        if (instance == null) {
            throw exception(PROCESS_INSTANCE_NOT_EXISTS);
        }

        if (task.isSuspended()) {
            throw new ServiceException("任务处于挂起状态");
        }
        // 获取流程定义信息
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionId(task.getProcessDefinitionId()).singleResult();
        // 获取所有节点信息
        Process process = repositoryService.getBpmnModel(processDefinition.getId()).getProcesses().get(0);
        // 获取全部节点列表，包含子节点
        Collection<FlowElement> allElements = FlowableUtils.getAllElements(process.getFlowElements(), null);
        // 获取当前任务节点元素
        FlowElement source = null;
        if (allElements != null) {
            for (FlowElement flowElement : allElements) {
                // 类型为用户节点
                if (flowElement.getId().equals(task.getTaskDefinitionKey())) {
                    // 获取节点信息
                    source = flowElement;
                }
            }
        }


        // 目的获取所有跳转到的节点 targetIds
        // 获取当前节点的所有父级用户任务节点
        // 深度优先算法思想：延边迭代深入
        List<UserTask> parentUserTaskList = FlowableUtils.iteratorFindParentUserTasks(source, null, null);
        if (parentUserTaskList == null || parentUserTaskList.size() == 0) {
            throw new ServiceException("当前节点为初始任务节点，不能驳回");
        }
        // 获取活动 ID 即节点 Key
        List<String> parentUserTaskKeyList = new ArrayList<>();
        parentUserTaskList.forEach(item -> parentUserTaskKeyList.add(item.getId()));
        // 2021.03.03修改：之前使用的是历史任务表获取数据改为使用历史活动表获取数据，因为发现并行情况下，只有一个流程完成的情况下，会出现单独停留在网关的情况，因此扭转时网关也是需要进行扭转的点，网关数据历史活动表才能获取到
        // 后续相关部分同步修改，以及工具类中部分同步修改
        // 获取全部历史节点活动实例，即已经走过的节点历史，数据采用开始时间升序
        List<HistoricActivityInstance> historicActivityIdList = historyService.createHistoricActivityInstanceQuery().processInstanceId(task.getProcessInstanceId()).orderByHistoricActivityInstanceStartTime().asc().list();
        // 数据清洗，将回滚导致的脏数据清洗掉
        List<String> lastHistoricTaskInstanceList = FlowableUtils.historicTaskInstanceClean(allElements, historicActivityIdList);
        // 此时历史任务实例为倒序，获取最后走的节点
        List<String> targetIds = new ArrayList<>();
        // 循环结束标识，遇到当前目标节点的次数
        int number = 0;
        StringBuilder parentHistoricTaskKey = new StringBuilder();
        for (String historicTaskInstanceKey : lastHistoricTaskInstanceList) {
            // 当会签时候会出现特殊的，连续都是同一个节点历史数据的情况，这种时候跳过
            if (parentHistoricTaskKey.toString().equals(historicTaskInstanceKey)) {
                continue;
            }
            parentHistoricTaskKey = new StringBuilder(historicTaskInstanceKey);
            if (historicTaskInstanceKey.equals(task.getTaskDefinitionKey())) {
                number++;
            }
            // 在数据清洗后，历史节点就是唯一一条从起始到当前节点的历史记录，理论上每个点只会出现一次
            // 在流程中如果出现循环，那么每次循环中间的点也只会出现一次，再出现就是下次循环
            // number == 1，第一次遇到当前节点
            // number == 2，第二次遇到，代表最后一次的循环范围
            if (number == 2) {
                break;
            }
            // 如果当前历史节点，属于父级的节点，说明最后一次经过了这个点，需要退回这个点
            if (parentUserTaskKeyList.contains(historicTaskInstanceKey)) {
                targetIds.add(historicTaskInstanceKey);
            }
        }


        // 目的获取所有需要被跳转的节点 currentIds
        // 取其中一个父级任务，因为后续要么存在公共网关，要么就是串行公共线路
        UserTask oneUserTask = parentUserTaskList.get(0);
        // 获取所有正常进行的执行任务的活动节点ID，这些任务不能直接使用，需要找出其中需要撤回的任务
        List<Execution> runExecutionList = runtimeService.createExecutionQuery().processInstanceId(task.getProcessInstanceId()).list();
        List<String> runActivityIdList = new ArrayList<>();
        runExecutionList.forEach(item -> {
            if (StringUtils.isNotBlank(item.getActivityId())) {
                runActivityIdList.add(item.getActivityId());
            }
        });
        // 需驳回任务列表
        List<String> currentIds = new ArrayList<>();
        // 通过父级网关的出口连线，结合 runExecutionList 比对，获取需要撤回的任务
        List<FlowElement> currentFlowElementList = FlowableUtils.iteratorFindChildUserTasks(oneUserTask, runActivityIdList, null, null);
        currentFlowElementList.forEach(item -> currentIds.add(item.getId()));


        // 规定：并行网关之前节点必须需存在唯一用户任务节点，如果出现多个任务节点，则并行网关节点默认为结束节点，原因为不考虑多对多情况
        if (targetIds.size() > 1 && currentIds.size() > 1) {
            throw new ServiceException("任务出现多对多情况，无法撤回");
        }

        // 2021.03.03修改：添加需撤回的节点为网关时，添加网关的删除信息
        AtomicReference<List<HistoricActivityInstance>> tmp = new AtomicReference<>();
        // 用于下面新增网关删除信息时使用
        String targetTmp = String.join(",", targetIds);
        // currentIds 为活动ID列表
        // currentExecutionIds 为执行任务ID列表
        // 需要通过执行任务ID来设置驳回信息，活动ID不行
        List<String> currentExecutionIds = new ArrayList<>();
        currentIds.forEach(currentId -> runExecutionList.forEach(runExecution -> {
            if (StringUtils.isNotBlank(runExecution.getActivityId()) && currentId.equals(runExecution.getActivityId())) {
                currentExecutionIds.add(runExecution.getId());
                // 查询当前节点的执行任务的历史数据
                tmp.set(historyService.createHistoricActivityInstanceQuery().processInstanceId(task.getProcessInstanceId()).executionId(runExecution.getId()).activityId(runExecution.getActivityId()).list());
                // 如果这个列表的数据只有 1 条数据
                // 网关肯定只有一条，且为包容网关或并行网关
                // 这里的操作目的是为了给网关在扭转前提前加上删除信息，结构与普通节点的删除信息一样，目的是为了知道这个网关也是有经过跳转的
                if (tmp.get() != null && tmp.get().size() == 1 && StringUtils.isNotBlank(tmp.get().get(0).getActivityType())
                        && ("parallelGateway".equals(tmp.get().get(0).getActivityType()) || "inclusiveGateway".equals(tmp.get().get(0).getActivityType()))) {
                    // singleResult 能够执行更新操作
                    // 利用 流程实例ID + 执行任务ID + 活动节点ID 来指定唯一数据，保证数据正确
                    historyService.createNativeHistoricActivityInstanceQuery().sql("UPDATE ACT_HI_ACTINST SET DELETE_REASON_ = 'Change activity to " + targetTmp + "'  WHERE PROC_INST_ID_='" + task.getProcessInstanceId() + "' AND EXECUTION_ID_='" + runExecution.getId() + "' AND ACT_ID_='" + runExecution.getActivityId() + "'").singleResult();
                }
            }
        }));
        // 设置驳回信息
        AtomicReference<Task> atomicCurrentTask = new AtomicReference<>();
        currentExecutionIds.forEach(item -> {
            atomicCurrentTask.set(taskService.createTaskQuery().executionId(currentExecutionIds.get(0)).singleResult());
            // 类型为网关时，获取用户任务为 null
            if (atomicCurrentTask.get() != null) {
                taskService.claim(reqVO.getId(), String.valueOf(userId));
                taskService.addComment(atomicCurrentTask.get().getId(), task.getProcessInstanceId(), "taskStatus", "reject");
                taskService.addComment(atomicCurrentTask.get().getId(), task.getProcessInstanceId(), "taskMessage", "已驳回");
                taskService.addComment(atomicCurrentTask.get().getId(), task.getProcessInstanceId(), "taskComment", reqVO.getComment());
            }
        });

        // 如果父级任务多于 1 个，说明当前节点不是并行节点，原因为不考虑多对多情况
        if (targetIds.size() > 1) {
            // 1 对 多任务跳转，currentIds 当前节点(1)，targetIds 跳转到的节点(多)
            runtimeService.createChangeActivityStateBuilder().processInstanceId(task.getProcessInstanceId()).moveSingleActivityIdToActivityIds(currentIds.get(0), targetIds).changeState();
        }
        // 如果父级任务只有一个，因此当前任务可能为网关中的任务
        if (targetIds.size() == 1) {
            // 1 对 1 或 多 对 1 情况，currentIds 当前要跳转的节点列表(1或多)，targetIds.get(0) 跳转到的节点(1)
            runtimeService.createChangeActivityStateBuilder().processInstanceId(task.getProcessInstanceId()).moveActivityIdsToSingleActivityId(currentIds, targetIds.get(0)).changeState();
        }

        // 更新任务拓展表为驳回
        BpmTaskExt bpmTaskExt = new BpmTaskExt();
        bpmTaskExt.setTaskId(task.getId());
        bpmTaskExt.setResult(BpmProcessInstanceResultEnum.BACK.getResult());
        bpmTaskExt.setRemark(reqVO.getComment());
        taskExtMapper.updateByTaskId(bpmTaskExt);
    }

    @Override
    public List<BpmTaskRespVO> findReturnTasks(String taskId) {
        // 当前任务 task
        Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
        // 获取流程定义信息
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionId(task.getProcessDefinitionId()).singleResult();
        // 获取所有节点信息，暂不考虑子流程情况
        Process process = repositoryService.getBpmnModel(processDefinition.getId()).getProcesses().get(0);
        Collection<FlowElement> flowElements = process.getFlowElements();
        // 获取当前任务节点元素
        UserTask source = null;
        if (flowElements != null) {
            for (FlowElement flowElement : flowElements) {
                // 类型为用户节点
                if (flowElement.getId().equals(task.getTaskDefinitionKey())) {
                    source = (UserTask) flowElement;
                }
            }
        }
        // 获取节点的所有路线
        List<List<UserTask>> roads = FlowableUtils.findRoad(source, null, null, null);
        // 可回退的节点列表
        List<UserTask> userTaskList = new ArrayList<>();
        for (List<UserTask> road : roads) {
            if (userTaskList.size() == 0) {
                // 还没有可回退节点直接添加
                userTaskList = road;
            } else {
                // 如果已有回退节点，则比对取交集部分
                userTaskList.retainAll(road);
            }
        }
        return CollectionUtils.convertList(userTaskList, userTask -> {
            BpmTaskRespVO bpmTaskRespVO = new BpmTaskRespVO();
            bpmTaskRespVO.setId(userTask.getId());
            bpmTaskRespVO.setName(userTask.getName());
            return bpmTaskRespVO;
        });
    }

    @Override
    public void flowReturn(String userId, BpmTaskBackReqVO reqVO) {
        String taskId = reqVO.getTaskId();
        String targetKey = reqVO.getTargetKey();

        // 校验任务存在
        Task task = checkTask(userId, taskId);
        // 校验流程实例存在
        ProcessInstance instance = processInstanceService.getProcessInstance(task.getProcessInstanceId());
        if (instance == null) {
            throw exception(PROCESS_INSTANCE_NOT_EXISTS);
        }

        if (task.isSuspended()) {
            throw new ServiceException("任务处于挂起状态");
        }
        // 获取流程定义信息
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionId(task.getProcessDefinitionId()).singleResult();
        // 获取所有节点信息，暂不考虑子流程情况
        Process process = repositoryService.getBpmnModel(processDefinition.getId()).getProcesses().get(0);
        // 获取全部节点列表，包含子节点
        Collection<FlowElement> allElements = FlowableUtils.getAllElements(process.getFlowElements(), null);
        // 获取当前任务节点元素
        FlowElement source = null;
        // 获取跳转的节点元素
        FlowElement target = null;
        if (allElements != null) {
            for (FlowElement flowElement : allElements) {
                // 当前任务节点元素
                if (flowElement.getId().equals(task.getTaskDefinitionKey())) {
                    source = flowElement;
                }
                // 跳转的节点元素
                if (flowElement.getId().equals(targetKey)) {
                    target = flowElement;
                }
            }
        }


        // 从当前节点向前扫描
        // 如果存在路线上不存在目标节点，说明目标节点是在网关上或非同一路线上，不可跳转
        // 否则目标节点相对于当前节点，属于串行
        Boolean isSequential = FlowableUtils.iteratorCheckSequentialReferTarget(source, targetKey, null, null);
        if (!isSequential) {
            throw new ServiceException("当前节点相对于目标节点，不属于串行关系，无法回退");
        }


        // 获取所有正常进行的执行任务节点的活动ID，这些任务不能直接使用，需要找出其中需要撤回的任务
        List<Execution> runExecutionList = runtimeService.createExecutionQuery().processInstanceId(task.getProcessInstanceId()).list();
        List<String> runActivityIdList = new ArrayList<>();
        runExecutionList.forEach(item -> {
            if (StringUtils.isNotBlank(item.getActivityId())) {
                runActivityIdList.add(item.getActivityId());
            }
        });
        // 需退回任务列表
        List<String> currentIds = new ArrayList<>();
        // 通过父级网关的出口连线，结合 runExecutionList 比对，获取需要撤回的任务
        List<FlowElement> currentFlowElementList = FlowableUtils.iteratorFindChildUserTasks(target, runActivityIdList, null, null);
        currentFlowElementList.forEach(item -> currentIds.add(item.getId()));


        // 2021.03.03修改：添加需撤回的节点为网关时，添加网关的删除信息
        AtomicReference<List<HistoricActivityInstance>> tmp = new AtomicReference<>();
        // currentIds 为活动ID列表
        // currentExecutionIds 为执行任务ID列表
        // 需要通过执行任务ID来设置驳回信息，活动ID不行
        List<String> currentExecutionIds = new ArrayList<>();
        currentIds.forEach(currentId -> runExecutionList.forEach(runExecution -> {
            if (StringUtils.isNotBlank(runExecution.getActivityId()) && currentId.equals(runExecution.getActivityId())) {
                currentExecutionIds.add(runExecution.getId());
                // 查询当前节点的执行任务的历史数据
                tmp.set(historyService.createHistoricActivityInstanceQuery().processInstanceId(task.getProcessInstanceId()).executionId(runExecution.getId()).activityId(runExecution.getActivityId()).list());
                // 如果这个列表的数据只有 1 条数据
                // 网关肯定只有一条，且为包容网关或并行网关
                // 这里的操作目的是为了给网关在扭转前提前加上删除信息，结构与普通节点的删除信息一样，目的是为了知道这个网关也是有经过跳转的
                if (tmp.get() != null && tmp.get().size() == 1 && StringUtils.isNotBlank(tmp.get().get(0).getActivityType())
                        && ("parallelGateway".equals(tmp.get().get(0).getActivityType()) || "inclusiveGateway".equals(tmp.get().get(0).getActivityType()))) {
                    // singleResult 能够执行更新操作
                    // 利用 流程实例ID + 执行任务ID + 活动节点ID 来指定唯一数据，保证数据正确
                    historyService.createNativeHistoricActivityInstanceQuery().sql("UPDATE ACT_HI_ACTINST SET DELETE_REASON_ = 'Change activity to " + targetKey + "'  WHERE PROC_INST_ID_='" + task.getProcessInstanceId() + "' AND EXECUTION_ID_='" + runExecution.getId() + "' AND ACT_ID_='" + runExecution.getActivityId() + "'").singleResult();
                }
            }
        }));
        // 设置驳回信息
        AtomicReference<Task> atomicCurrentTask = new AtomicReference<>();
        currentExecutionIds.forEach(item -> {
            atomicCurrentTask.set(taskService.createTaskQuery().executionId(currentExecutionIds.get(0)).singleResult());
            // 类型为网关时，获取用户任务为 null
            if (atomicCurrentTask.get() != null) {
                taskService.claim(reqVO.getTaskId(), String.valueOf(userId));
                taskService.addComment(atomicCurrentTask.get().getId(), task.getProcessInstanceId(), "taskStatus", "return");
                taskService.addComment(atomicCurrentTask.get().getId(), task.getProcessInstanceId(), "taskMessage", "已退回");
                taskService.addComment(atomicCurrentTask.get().getId(), task.getProcessInstanceId(), "taskComment", "流程回退到" + atomicCurrentTask.get().getName() + "节点");
            }
        });

        // 1 对 1 或 多 对 1 情况，currentIds 当前要跳转的节点列表(1或多)，targetKey 跳转到的节点(1)
        runtimeService.createChangeActivityStateBuilder().processInstanceId(task.getProcessInstanceId()).moveActivityIdsToSingleActivityId(currentIds, targetKey).changeState();

        // 更新任务拓展表为驳回
        BpmTaskExt bpmTaskExt = new BpmTaskExt();
        bpmTaskExt.setTaskId(task.getId());
        bpmTaskExt.setResult(BpmProcessInstanceResultEnum.BACK.getResult());
        bpmTaskExt.setRemark(reqVO.getComment());
        taskExtMapper.updateByTaskId(bpmTaskExt);

    }

    @Override
    public void updateBusinessFormFlow(String userId, String taskId, Map<String, Object> processInstanceVariables) {
        // 校验任务存在
        Task task = checkTask(userId, taskId);
        // 校验流程实例存在
        ProcessInstance instance = processInstanceService.getProcessInstance(task.getProcessInstanceId());
        if (instance == null) {
            throw exception(PROCESS_INSTANCE_NOT_EXISTS);
        }

        boolean checkFirstNode = checkTaskCanUpdate(task);
        if (!checkFirstNode) {
            throw exception(TASK_IS_NOT_START_TASK);
        }

        // 完成任务，审批通过
        if (processInstanceVariables != null) {
            for (Map.Entry<String, Object> entry : processInstanceVariables.entrySet()) {
                instance.getProcessVariables().put(entry.getKey(), entry.getValue());
            }
        }
        taskService.claim(task.getId(), String.valueOf(userId));
        taskService.complete(task.getId(), instance.getProcessVariables());
    }

    /**
     * 根据taskid判断该task是否第一个节点
     * 如果是的话，可以修改表单
     */
    private boolean checkTaskCanUpdate(Task task) {
        boolean result = false;
        // 获取流程定义信息
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionId(task.getProcessDefinitionId()).singleResult();
        // 获取所有节点信息，暂不考虑子流程情况
        Process process = repositoryService.getBpmnModel(processDefinition.getId()).getProcesses().get(0);
        Collection<FlowElement> flowElements = process.getFlowElements();
        // 获取当前任务节点元素
        UserTask source = null;
        if (flowElements != null) {
            for (FlowElement flowElement : flowElements) {
                // 类型为用户节点
                if (flowElement.getId().equals(task.getTaskDefinitionKey())) {
                    source = (UserTask) flowElement;
                    break;
                }
            }
        }
        if (source != null) {
            if (source instanceof org.flowable.bpmn.model.Task) {
                List<SequenceFlow> sequenceFlows = source.getIncomingFlows();
                if (sequenceFlows != null && 1 == sequenceFlows.size()) {
                    SequenceFlow sequenceFlow = sequenceFlows.get(0);
                    FlowElement startFlowElement = sequenceFlow.getSourceFlowElement();
                    if (startFlowElement != null && startFlowElement instanceof StartEvent) {
                        result = true;
                    }

                }
            }
        }

        return result;
    }


    @Override
    public BpmTaskRespVO getTaskDetailById(String userId, String taskId) {
        BpmTaskRespVO bpmTaskRespVO = null;
        // 校验任务存在
        Task task = getTask(taskId);
        if (task == null) {
            throw exception(TASK_COMPLETE_FAIL_NOT_EXISTS);
        }
        // 校验流程实例存在
        ProcessInstance instance = processInstanceService.getProcessInstance(task.getProcessInstanceId());
        if (instance == null) {
            throw exception(PROCESS_INSTANCE_NOT_EXISTS);
        }
        // 获得任务列表
        HistoricTaskInstance historicTaskInstance = getHistoricTask(taskId);

        if (historicTaskInstance != null) {
            List<HistoricTaskInstance> tasks = new ArrayList<>();
            tasks.add(historicTaskInstance);
            List<BpmTaskRespVO> bpmTaskRespVOS = getBpmTaskRespVOSByTasks(tasks, instance.getProcessInstanceId());
            bpmTaskRespVO = bpmTaskRespVOS.get(0);
            Task taskCheck = taskService.createTaskQuery().taskId(taskId)
                    .or().taskCandidateUser(String.valueOf(userId)).taskAssignee(String.valueOf(userId)).endOr().singleResult();
            if (taskCheck != null) {
                bpmTaskRespVO.setTaskCheck(true);
                if (checkTaskCanUpdate(task)) {
                    bpmTaskRespVO.setTaskCheckUpdateForm(true);
                }
                BpmProcessDefinitionExt processDefinitionExt = processDefinitionService.getProcessDefinitionExt(
                        instance.getProcessDefinitionId());
                Integer formCanReturn = processDefinitionExt.getFormCanReturn();
                if (formCanReturn != null && 1 == formCanReturn.intValue()) {
                    bpmTaskRespVO.setTaskCanReturn(true);
                }

            }
        }

        return bpmTaskRespVO;
    }

}
